package fallingGame;

import java.awt.Event;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

/**
 * 
 * <p>
 * Title: FallingGame
 * </p>
 * <p>
 * Description: CS 343 Course Project FallingGame is the view in the Model View
 * Controller (MVC). This is the main class that is the GUI. This class starts
 * the other threads run.
 * </p>
 * 
 * @author Nathanial Howard, Joe Costello, Dustin Schwingle, Jared Potter
 * @email nathanial.howard@my.uwrf.edu, anthony.costello@my.uwrf.edu,
 *        dustin.schwingle@my.uwrf.edu, jared.potter@my.uwrf.edu
 * @date December 01, 2011
 * @team Group 2
 * 
 */
public class FallingGame extends JPanel implements ActionListener, Runnable {

	private static final long serialVersionUID = 1L;
	private GameModel model;
	private JFrame frame;
	private boolean runForeverView;

	// Menu and Menu Items
	private JMenuBar menu;
	private JMenu fileMenu, optionsMenu, helpMenu;
	private JMenuItem newGame, viewHighScores, clearHighScores, quit, help,
			about;
	private JCheckBoxMenuItem WASDMode, sound;

	// Text and Label for score
	private JTextField scoreText;
	private JLabel scoreLabel;

	// scenery
	private Image backgroundImg, mapImg;
	// your character
	private Image heroLeftImg, heroRightImg;
	// enemy objects
	private Image brickImg;
	// goal object
	private Image starImg;

	private BufferedImage bufferedImage;
	private Graphics2D graphics;

	/**
	 * Falling Game default constructor - sets up the GUI
	 * 
	 * @throws InterruptedException
	 * @ensure GUI will be set up
	 */
	public FallingGame() throws InterruptedException {

		model = new GameModel();
		frame = new JFrame();
		runForeverView = true;

		// TextField to show score
		scoreText = new JTextField("0");
		scoreText.setBounds(700, 0, 100, 25);
		scoreText.setEnabled(false);
		frame.add(scoreText);

		// label for score
		scoreLabel = new JLabel("Score:");
		scoreLabel.setBounds(650, 0, 50, 25);
		frame.add(scoreLabel);

		// load images
		backgroundImg = loadImg("background.png");
		mapImg = loadImg("map.png");
		heroLeftImg = loadImg("hero_left.png");
		heroRightImg = loadImg("hero_right.png");
		brickImg = loadImg("brick.png");
		starImg = loadImg("star.png");

		bufferedImage = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
		graphics = bufferedImage.createGraphics();
		setDoubleBuffered(true);

		// Graphical components
		frame.add(this);
		frame.setTitle("FallingGame - Group 2 - Howard/Costello/Schwingle/Potter");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(800, 600);
		frame.setLocationRelativeTo(null);
		frame.setResizable(false);

		menu = new JMenuBar();
		// file menu
		fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);
		menu.add(fileMenu);
		newGame = new JMenuItem("New Game");
		newGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
				Event.CTRL_MASK));
		newGame.addActionListener(this);
		fileMenu.add(newGame);

		viewHighScores = new JMenuItem("View High Scores");
		viewHighScores.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H,
				Event.CTRL_MASK));
		viewHighScores.addActionListener(this);
		fileMenu.add(viewHighScores);

		clearHighScores = new JMenuItem("Clear High Scores");
		clearHighScores.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
				Event.CTRL_MASK));
		clearHighScores.addActionListener(this);
		fileMenu.add(clearHighScores);

		quit = new JMenuItem("Quit");
		quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
				Event.CTRL_MASK));
		quit.addActionListener(this);
		fileMenu.add(quit);

		// options menu
		optionsMenu = new JMenu("Options");

		WASDMode = new JCheckBoxMenuItem("WASD Mode");
		WASDMode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W,
				Event.CTRL_MASK));
		WASDMode.addActionListener(this);
		optionsMenu.add(WASDMode);

		sound = new JCheckBoxMenuItem("Sound");
		sound.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
				Event.CTRL_MASK));
		sound.addActionListener(this);
		optionsMenu.add(sound);

		optionsMenu.setMnemonic(KeyEvent.VK_O);
		menu.add(optionsMenu);

		// help menu
		helpMenu = new JMenu("Help");
		helpMenu.setMnemonic(KeyEvent.VK_H);
		menu.add(helpMenu);

		help = new JMenuItem("How To Play");
		help.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P,
				Event.CTRL_MASK));
		help.addActionListener(this);
		helpMenu.add(help);

		about = new JMenuItem("About");
		about.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
				Event.CTRL_MASK));
		about.addActionListener(this);
		helpMenu.add(about);

		frame.setJMenuBar(menu);

		frame.addKeyListener(model.getInput());

		frame.setVisible(true);
	}

	/**
	 * run - start a new thread that updates the score text field and repaints
	 * the GUI
	 * 
	 * @ensure a new thread will be started
	 */
	public void run() {
		// start the model
		model.start();

		// run the thread forever
		while (runForeverView) {
			// update the GUI only if the game is not over
			while (!model.isGameOver()) {
				scoreText.setText("" + model.getScore());
				frame.repaint();
				try {
					Thread.sleep(15);
				} catch (InterruptedException e) {
				}
			}
			try {
				Thread.sleep(15);
			} catch (InterruptedException e) {
			}
		}

		model.setRunForeverModel(false);
	}

	/**
	 * actionPerformed - listener for the menu
	 * 
	 * @ensure - GUI will be updated according to what menu option is pressed
	 */
	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getSource() == newGame) {
			/*
			 * remove the current key listener when a new game is started and
			 * add a new key listener so movement from the previous game is not
			 * carried over
			 */
			frame.removeKeyListener(model.getInput());
			model.newGame();
			frame.addKeyListener(model.getInput());
			if (WASDMode.isSelected())
				model.getInput().setWasdMode(true);
			else
				model.getInput().setWasdMode(false);
		}
		if (arg0.getSource() == viewHighScores) {
			new HighScore().showHighScores();
		}
		if (arg0.getSource() == clearHighScores) {
			int clear = JOptionPane.showConfirmDialog(null,
					"Are you sure you want to clear the High Score table?",
					"Clear High Score Table?", JOptionPane.YES_NO_OPTION);
			if (clear == JOptionPane.YES_OPTION)
				new HighScore().clearHighScores();
		}
		if (arg0.getSource() == quit) {
			System.exit(0);
		}
		if (arg0.getSource() == WASDMode) {
			if (WASDMode.isSelected())
				model.getInput().setWasdMode(true);
			else
				model.getInput().setWasdMode(false);
		}
		if (arg0.getSource() == sound) {
			if (sound.isSelected())
				model.getSound().playMusic();
			else
				model.getSound().stopAll();
		}
		if (arg0.getSource() == help) {
			JOptionPane
					.showMessageDialog(
							null,
							"Falling Game\n\nAvoid the falling bricks. "
									+ "They kill you when you touch them in any way. "
									+ "Then it's game over!\n\n"
									+ ""
									+ "Survive for a long time because you get 10 points per second."
									+ "\n"
									+ "Are you good enough to collect stars too? 100 bonus points each!\n"
									+ "\n"
									+ "Use arrow keys to move and jump; alternatively WASD keys (if mode is enabled).\n"
									+ "\n"
									+ "Get high scores and compare with others!"
									+ "\n" + "Good luck!", "Help",
							JOptionPane.QUESTION_MESSAGE);
		}
		if (arg0.getSource() == about) {
			JOptionPane
					.showMessageDialog(
							null,
							"Falling Game\n\nCreated By:\nNate Howard,"
									+ " Dustin Schwingle, Joe Costello, Jared Potter\n\nCSIS 343",
							"About", JOptionPane.PLAIN_MESSAGE);
		}
	}

	/**
	 * paint(Graphics g_in) draws every image to graphics 2d and applies it to
	 * the screen. This method is never explicitly called. It is called by
	 * repaint().
	 * 
	 * @require graphics of a JFrame
	 * @ensure GUI is updated with new graphics
	 */
	public void paint(Graphics g_in) {

		Graphics2D g2 = (Graphics2D) g_in;

		// Draw scenery
		graphics.drawImage(backgroundImg, 0, 0, null);
		graphics.drawImage(mapImg, 144, 472, null);

		// Draw the player if active
		if (model.getHero().isActive()) {

			// draw hero right
			if (model.getHero().isFacingRight()) {
				graphics.drawImage(heroRightImg, (int) model.getHero().getX(),
						(int) model.getHero().getY(), null);
			}
			// draw hero left
			else {
				graphics.drawImage(heroLeftImg, (int) model.getHero().getX(),
						(int) model.getHero().getY(), null);
			}
		}

		// Draw active bricks
		for (Brick b : model.getBricks()) {
			if (b.isActive()) {
				graphics.drawImage(brickImg, (int) b.getX(), (int) b.getY(),
						null);
			}
		}

		// Draw active star object
		if (model.getStar().isActive()) {
			graphics.drawImage(starImg, (int) model.getStar().getX(),
					(int) model.getStar().getY(), null);
		}

		g2.drawImage(bufferedImage, 0, 0, this);
	}

	/**
	 * loadImg(String filename) loads the image fileName
	 * 
	 * @param fileName
	 * @return a new Image
	 */
	private Image loadImg(String fileName) {
		return (new ImageIcon(this.getClass().getResource(fileName)))
				.getImage();
	}

	/**
	 * main - starts FallingGame
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			new Thread(new FallingGame()).start();
		} catch (Exception e) {
			System.err.println("Caught Exception:\n");
			e.printStackTrace();
		}

	}

	/**
	 * setRunForeverView
	 * 
	 * setter for runForeverView
	 * 
	 * @param runForeverView
	 * @ensure this.runForeverView = runForeverView
	 */
	public void setRunForeverView(boolean runForeverView) {
		this.runForeverView = runForeverView;
	}
}